package MyGui;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
import java.util.*;
import java.sql.*;
import java.text.NumberFormat;

import Element.*;
import Element.Geom.*;
import BackGround.*;
import DataAccess.*;
import DistSys.*;

public class DrawingPanel
  extends JPanel  //JLayeredPane//
  implements MouseListener, MouseMotionListener{

  MainFrame mainFrame;  // MainApplet

  //for debug
  static        int cntrRepaint=0; //for debug only

  // attributes
  static  final int DRAG_NONE=0, DRAG_NODE_ONE=1, DRAG_NODE_TWO=2, DRAG_ELEM =4;
  static  final int INIT_VIEW_WIDTH=4000, INIT_VIEW_HEIGHT=3000;

//  private Image[] imgBuf;
  private Image image;
          boolean needRedraw=true;
          boolean bZoomAll = false;

  private Point ptMsPressed  =new Point();
  private Point ptMsReleased =new Point();
  private Point ptMsDragged  =new Point();
  private Point ptMsDraggedPrev =new Point(0,0);

//  private Point prevLeftTop = new Point(0,0);
  private Rectangle prevRect = new Rectangle(INIT_VIEW_WIDTH/2,INIT_VIEW_HEIGHT/2,0,0);

  private int draggingStatus; // for lines, busses only;

  private Rectangle rectShadingBox = new Rectangle();
  private Rectangle rectScaleBox = new Rectangle();

  public DrawingPanel() { //throws MalformedURLException{

      addMouseListener( this);
      addMouseMotionListener(this);

      if( image ==null){
          Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
          image= new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); //createImage( dim.width, dim.height ); //
      }
      needRedraw = true;

      this.setDoubleBuffered(true); //by default
      setBackground(Color.white);
      setPreferredSize(new Dimension(INIT_VIEW_WIDTH, INIT_VIEW_HEIGHT));
  }

  /*========================================================
  Low-level mouse event processing methods
  Public methods
      1. public void mousePressed(MouseEvent evt)
      2. public void mouseDragged(MouseEvent evt)
      3. public void mouseReleased(MouseEvent evt)
      4. public void mouseClicked(MouseEvent evt)
      5. public void mouseEntered(MouseEvent evt)
      6. public void mouseExited(MouseEvent evt)
      7. public void mouseMoved(MouseEvent evt)
  ========================================================*/

  public void mousePressed(MouseEvent evt){
      ptMsPressed.setLocation( evt.getX(), evt.getY());
      ptMsDraggedPrev.setLocation(0,0); // reset ptMsDraggedPrev

      int mod = evt.getModifiers();
      if( mod != InputEvent.BUTTON1_MASK  &&
          mod != (InputEvent.BUTTON1_MASK + InputEvent.CTRL_MASK) &&
          mod != (InputEvent.BUTTON1_MASK + InputEvent.SHIFT_MASK) &&
          mod != (InputEvent.BUTTON3_MASK) //right ms pressed
          ){ return;} //not left mouse

      ButtonPanel bp= mainFrame.btnPanel;
      int btnSelection = bp.getIdOfDrawingBtn();
      if( mod== InputEvent.BUTTON3_MASK ){ //if right mouse pressed
          if( btnSelection!= bp.BTN_ARROW){ return; } //if not in ARROW mode
          unselectAndUpdateScreen();
          Sys.focusElem = Sys.findElem( ptMsPressed.x, ptMsPressed.y);
          if( Sys.focusElem != null){
              Sys.addElemToSelection( Sys.focusElem );
              addToScreenImage( Sys.focusElem );
          }
          repaint();
          //mainFrame.statusBar.updateText( );
          return;
      }

      if( Sys.isInsertNodeMode == true ){  //for insert nodes
          insertNodeForLineOrCable(evt.getX(),evt.getY());
          Sys.isInsertNodeMode = false;
          mainFrame.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      } else if( btnSelection == bp.BTN_LINE ||
          btnSelection == bp.BTN_CABLE){ //line-drawing finished
          if( Sys.prjInfo.iActiveLayer == Sys.prjInfo.BG_LINE_LAYER ){
              addBgLine( MouseEvent.MOUSE_PRESSED );
          }else{
              addLineOrCable( MouseEvent.MOUSE_PRESSED);  // step 1 of drawLine()
          }
      } else if(btnSelection == bp.BTN_BUS )     {
          addBus( MouseEvent.MOUSE_PRESSED);
      } else if(btnSelection == bp.BTN_GROUP )     {
          addGroup( MouseEvent.MOUSE_PRESSED);
      }
      else if(btnSelection == bp.BTN_SOURCE)  { addSource(); }
      else if(btnSelection == bp.BTN_DG )     { addDG();     }
      else if(btnSelection == bp.BTN_TXFM )   { addTxfm();   }
      else if(btnSelection == bp.BTN_TXFM_3 ) { addTxfm3();   }
      else if(btnSelection == bp.BTN_REG )    { addReg();    }

      else if(btnSelection == bp.BTN_CKT_BK ) { addBreaker();     }
      else if(btnSelection == bp.BTN_BK )     { addDrtBreaker();  }
      else if(btnSelection == bp.BTN_RECL )   { addRecloser();}
      else if(btnSelection == bp.BTN_SWITCH ) { addSwitch();  }
      else if(btnSelection == bp.BTN_FUSE )   { addFuse();    }
      else if(btnSelection == bp.BTN_SECT )   { addSect();    }
      else if(btnSelection == bp.BTN_SWITCH_3){ addSwitch3(); }
      else if(btnSelection == bp.BTN_SWITCH_GND ) { addSwitchGnd();  }

      else if(btnSelection == bp.BTN_LOAD )   { addLoad();      }
      else if(btnSelection == bp.BTN_SERIES_CAP )   { addSeriesCap();     }
      else if(btnSelection == bp.BTN_CAP )    { addCap();       }
      else if(btnSelection == bp.BTN_CT )     { addCT();        }
      else if(btnSelection == bp.BTN_MOTOR )  { addMotor();     }
      else if(btnSelection == bp.BTN_ARRESTER){ addArrester();  }
      else if(btnSelection == bp.BTN_REACTOR ){ addReactor();   }
      else if(btnSelection == bp.BTN_PT )     { addPT();        }

      else if(btnSelection==bp.BTN_ARROW ){
          //Possiblitiies: move multiple elem; move a Group; move an elem;
          //stretch a group; stretch an elem; selection
          Sys.focusElem = Sys.findElem( ptMsPressed.x, ptMsPressed.y);
          mainFrame.statusBar.updateText();

          if( Sys.focusElem == null ){
              boolean b =evt.isShiftDown()|| evt.isControlDown();
              selectElemInRect( MouseEvent.MOUSE_PRESSED, b);
          }
          else{
              if( Sys.focusElem instanceof Group ){
                  //find all elem in this group
                  Group group = (Group) Sys.focusElem;
                  Rectangle rect = mainFrame.scrollPane.getViewport().getViewRect();
                  if( rect.contains( group.getElemGeomObj().frame ) ){
                      group.findContainedElemFrom(Sys.screenElemList);//to improve performance
                  }
                  else group.findContainedElemFrom(Sys.elemList);
              }

              // can't judge with # of elememnts in Sys.selectedList
              // becoz there may be multiple elements selected while mouse pointing only one
              if( evt.isControlDown() || evt.isShiftDown() ){ //for multiple elem
                  //no clear of previously selected elem
                  if(Sys.focusElem instanceof Group ){
                     Sys.addElemInAGroupIntoSelection((Group)Sys.focusElem);
                  }
                  moveManyElem( MouseEvent.MOUSE_PRESSED );
              }
              else { //for a single elem
                  if( Sys.focusElem instanceof Group ){
                      AbsElemGeom g = Sys.focusElem.getElemGeomObj();
                      if ( g.isStartPoint(ptMsPressed, SysConst.SNAP_SIZE) == true ){
                          draggingStatus = DRAG_NODE_ONE;
                      }else if( g.isEndPoint(ptMsPressed, SysConst.SNAP_SIZE) == true ){
                          draggingStatus = DRAG_NODE_TWO;
                      }else { draggingStatus = DRAG_ELEM;}

                      if( draggingStatus == DRAG_ELEM ){
                          Sys.clearSelection();  //But KEEP the FOCUS ELEM
                          Sys.addElemInAGroupIntoSelection((Group)Sys.focusElem);
                          moveManyElem( MouseEvent.MOUSE_PRESSED );
                      }
                      else{//actually stretch the group
                          moveOneElem(MouseEvent.MOUSE_PRESSED);
                      }
                  }else{ //otherwise move one elem only
                      //System.out.println("2. Elem="+Sys.focusElem );
                      moveOneElem( MouseEvent.MOUSE_PRESSED);
                  }
              }
          }
      }
      else if(btnSelection == bp.BTN_ZOOM_WIN ){
          //do nothing
      }
  }

  //implemets mouse motion event
  public void mouseDragged(MouseEvent evt){
      ptMsDragged.setLocation(evt.getX(), evt.getY());

      int mod = evt.getModifiers();
      if( mod != InputEvent.BUTTON1_MASK  &&
          mod != (InputEvent.BUTTON1_MASK + InputEvent.CTRL_MASK) &&
          mod != (InputEvent.BUTTON1_MASK + InputEvent.SHIFT_MASK)
        ){ return;} //not left mouse

      ButtonPanel bp=mainFrame.btnPanel;
      if( bp.getIdOfDrawingBtn() == bp.BTN_LINE ||
          bp.getIdOfDrawingBtn() == bp.BTN_CABLE){ //in drawing a line
          if( Sys.prjInfo.iActiveLayer == Sys.prjInfo.BG_LINE_LAYER ){
              addBgLine( MouseEvent.MOUSE_DRAGGED );
          }else{
              addLineOrCable( MouseEvent.MOUSE_DRAGGED);
          }
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_BUS){
          addBus(  MouseEvent.MOUSE_DRAGGED );
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_GROUP )     {
          addGroup( MouseEvent.MOUSE_DRAGGED);
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_ZOOM_WIN){
          zoomIntoWindow(  MouseEvent.MOUSE_DRAGGED );
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_ARROW ){ //arrow button
          int num = Sys.selectedList.size();
          if( Sys.focusElem==null){ selectElemInRect(MouseEvent.MOUSE_DRAGGED, true); }
          else if( num==1){ moveOneElem(MouseEvent.MOUSE_DRAGGED); }
          else            { moveManyElem(MouseEvent.MOUSE_DRAGGED); }
      }//end of else for { BTN_ARROW }

      ptMsDraggedPrev.setLocation(ptMsDragged);
  }// end of mousedDragged()


  public void mouseReleased(MouseEvent evt){
      ptMsReleased.setLocation(evt.getX(),evt.getY());
      ptMsDraggedPrev.setLocation(0,0);
      //draggingStatus = DRAG_NONE;

      int mod = evt.getModifiers();
      if( mod != InputEvent.BUTTON1_MASK  &&
          mod != (InputEvent.BUTTON1_MASK + InputEvent.CTRL_MASK) &&
          mod != (InputEvent.BUTTON1_MASK + InputEvent.SHIFT_MASK)
          ){ return;} //not left mouse

      ButtonPanel bp= mainFrame.btnPanel;
      if( bp.getIdOfDrawingBtn() == bp.BTN_LINE ||
          bp.getIdOfDrawingBtn() == bp.BTN_CABLE){ //line-drawing finished
          if( Sys.prjInfo.iActiveLayer == Sys.prjInfo.BG_LINE_LAYER ){
              addBgLine( MouseEvent.MOUSE_RELEASED );
          }else{
              addLineOrCable( MouseEvent.MOUSE_RELEASED);
          }
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_BUS ){
          addBus(  MouseEvent.MOUSE_RELEASED );
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_GROUP )     {
          addGroup( MouseEvent.MOUSE_RELEASED);
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_ZOOM_WIN ){
          zoomIntoWindow(  MouseEvent.MOUSE_RELEASED );
      }
      else if(bp.getIdOfDrawingBtn() == bp.BTN_ARROW){
          int num = Sys.selectedList.size();
          if( Sys.focusElem==null){ selectElemInRect(MouseEvent.MOUSE_RELEASED, true); }
          else if( num==1){ moveOneElem(MouseEvent.MOUSE_RELEASED); }
          else            { moveManyElem(MouseEvent.MOUSE_RELEASED); }
      }
      //this.dispatchEvent(new ActionEvent(bp.btnArray[bp.BTN_ARROW], ActionEvent.ACTION_PERFORMED,""));
  }//end of mouseReleased()

  public void mouseClicked(MouseEvent evt){
      Point p=this.mainFrame.scrollPane.getViewport().getViewPosition();
      int x=p.x;
      int y=p.y;
      Point msPt = evt.getPoint();

      if(evt.getClickCount()==2 && evt.getModifiers()==InputEvent.BUTTON1_MASK){
      //left mouse double clicked
          if( Sys.prjInfo.showLegend && rectShadingBox.contains(msPt) ){
              if (Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_ZONE) {
                  mainFrame.menuBar.showOptionsDlg(OptionsDlg.TAB_ZONE_DISPLAY);
              } else if (Sys.prjInfo.shadingCat >= 0) {
                  mainFrame.menuBar.showEditShadingDlg();
              }
          } else if( Sys.prjInfo.showScale && rectScaleBox.contains(msPt) ){
              mainFrame.menuBar.showOptionsDlg( OptionsDlg.TAB_SCALE );
          } else{
              mainFrame.menuBar.showElemInfoForAnyCase();
          }
      }
      else if( evt.getClickCount()==1 && evt.getModifiers()==InputEvent.BUTTON3_MASK){
      //popup menu -- right mouse clicked
          //inside legend area
          if( Sys.prjInfo.showLegend && rectShadingBox.contains(msPt) ){
              return;
          } else if( Sys.prjInfo.showScale && rectScaleBox.contains(msPt) ){
              return;
          }else{
              if( Sys.focusElem == null ){
                  mainFrame.popupMenu2.show(  evt.getComponent(),
                      ptMsReleased.x, ptMsReleased.y);
              } else {
                  mainFrame.popupMenu.show( evt.getComponent(),
                      ptMsReleased.x, ptMsReleased.y);
              }
          }
      }
  }

  public void mouseEntered(MouseEvent evt){
    //setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
  }
  public void mouseExited(MouseEvent evt){
    //setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  }

  public void mouseMoved(MouseEvent evt){
  };

  //end of MouseListener();



  /*=========================================================
  High-level event processing methods
  Private Methods
      1. private void selectElemInRect(int mouseEventId )
      2. private void moveOneElem ( int mouseEventId )
      3. private void moveManyElem ( int mouseEventId )
      4. private void zoomIntoWindow( int mouseEventId )
      5~. private void add***()

  ==========================================================*/

  //selecting elements in a mouse-dragged rectangle
  //boolean isShiftOrCtrlDown only takes effect at MOUSE_PRESSED section.
  private void selectElemInRect(int mouseEventId, boolean isShiftOrCtrlDown){
      Graphics g = this.getGraphics();

      if( mouseEventId == MouseEvent.MOUSE_PRESSED ){
          int sz=Sys.selectedList.size();
          if( isShiftOrCtrlDown == false ) {
              unselectAndUpdateScreen();
              repaint();
          }
      }
      else if (mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          g.setXORMode(Color.yellow);
          Sys.draggedRect = findSelectedRect(ptMsPressed, ptMsDragged);
          Rectangle prevRect;
          if( ptMsDraggedPrev.x!=0){ //not the first drag; need to be XORed
              prevRect = findSelectedRect(ptMsPressed, ptMsDraggedPrev);
              g.drawRect(prevRect.x-1, prevRect.y-1, prevRect.width+2, prevRect.height+2);
              g.drawRect(prevRect.x, prevRect.y, prevRect.width, prevRect.height);
              g.drawRect(prevRect.x+1, prevRect.y+1, prevRect.width-2, prevRect.height-2);
              //draw 3 rectangles actually; sort of bold.
          }
          g.drawRect( Sys.draggedRect.x-1, Sys.draggedRect.y-1,
                       Sys.draggedRect.width+2, Sys.draggedRect.height+2);
          g.drawRect( Sys.draggedRect.x, Sys.draggedRect.y,
                       Sys.draggedRect.width, Sys.draggedRect.height);
          g.drawRect( Sys.draggedRect.x+1, Sys.draggedRect.y+1,
                       Sys.draggedRect.width-2, Sys.draggedRect.height-2);
          //don't repaint any more; //repaint();
          g.setPaintMode();
      }
      else if(mouseEventId == MouseEvent.MOUSE_RELEASED ){
          Sys.draggedRect = findSelectedRect( ptMsReleased, ptMsPressed);
          if( Sys.draggedRect.width < 2 &&  Sys.draggedRect.height< 2 ){
          //to avoid the case: users add an element.
          //In this case, this event will be also executed.
              //JOptionPane.showMessageDialog(mainFrame, "Selected Area Too Small!");
              Sys.draggedRect.setBounds(0,0,0,0);
              if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER){
                  needRedraw = true;  //becoz BgImg may cover foreground elem.
              }
              repaint(); //no redraw
              return;  // rectangle too small
          }
          Sys.findElemInRect( Sys.draggedRect );  //populate Sys.selectedElemList
          addToScreenImage( Sys.selectedList );   //cover previous drawings
          repaint();
          Sys.draggedRect.setBounds(0,0,0,0); // reset the draggedRect
      }
  }


  //Sys.focusElem is always the only elem in Sys.selectedList
  private void moveOneElem( int mouseEventId ){
      AbsElemGeom g;
      Graphics graphics = this.getGraphics();
      //System.out.println("3. Elem="+Sys.focusElem );

      if( mouseEventId == MouseEvent.MOUSE_PRESSED ){
          unselectAndUpdateScreen();
          Sys.addElemToSelection( Sys.focusElem );
          addToScreenImage( Sys.selectedList );
          repaint();

          if(Sys.prjInfo.freeze){ return; } //is freezed?

          g = Sys.focusElem.getElemGeomObj();
          if ( g.isStartPoint(ptMsPressed, SysConst.SNAP_SIZE) == true ){
                  draggingStatus = DRAG_NODE_ONE;
          }else if( g.isEndPoint(ptMsPressed, SysConst.SNAP_SIZE) == true ){
                  draggingStatus = DRAG_NODE_TWO;
          }else draggingStatus = DRAG_ELEM;
      }
      else if (mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          if(Sys.prjInfo.freeze){ return; } //is freezed?

          graphics.setXORMode( Color.blue );

          int offsetX, offsetY;
          if( ptMsDraggedPrev.x ==0 && ptMsDraggedPrev.y == 0){// first mouseDrag event
              offsetX = ptMsDragged.x - ptMsPressed.x;
              offsetY = ptMsDragged.y - ptMsPressed.y;
          } else{
              offsetX = ptMsDragged.x - ptMsDraggedPrev.x;
              offsetY = ptMsDragged.y - ptMsDraggedPrev.y;
              Sys.focusElem.drawForMove( graphics);// XORed
          }

          //during dragging, only screen (x,y) is changed
          //In other words, screen and stored (X,Y)'s are NOT synchronized!
          g =Sys.focusElem.getElemGeomObj();
          if      (draggingStatus==DRAG_NODE_ONE) { g.x1 += offsetX;  g.y1 += offsetY; }
          else if (draggingStatus==DRAG_NODE_TWO) { g.x2 += offsetX;  g.y2 += offsetY; }
          else {    //drag the whole element
              g.x1 += offsetX;  g.y1 += offsetY;
              g.x2 += offsetX;  g.y2 += offsetY;
          }

          if( g instanceof ThreeNodeElemGeom){ // node 3; for all dragging status
              ((ThreeNodeElemGeom)g).findNode3BasedonScreen(); //displacement of node 3 != (+offsetX,Y)
          }
          Sys.focusElem.getElemGeomObj().calcFrame();

          Sys.focusElem.drawForMove( graphics );//no drawing of text; XORed
          graphics.setPaintMode();
      }
      else if(mouseEventId == MouseEvent.MOUSE_RELEASED ){
          if(Sys.prjInfo.freeze /*|| draggingStatus==DRAG_NONE*/ ){ return; } //is freezed?

          //no move or just a mouse click
          if( ptMsReleased.x==ptMsPressed.x && ptMsReleased.y==ptMsPressed.y){
              if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER ){
                  needRedraw = true;  //becoz BgImg may cover foreground elems
                  repaint();
              }
              return;
          }

          //Must appear before sync stored(X,Y)
          Vector vMovedList = ElemForUndoRedo.createEfurForVector(Sys.selectedList);

          //if Sys.focusElem has been MOVED
          // snap screen value & synchronize stored value
          int offsetXStored = ptMsReleased.x - ptMsPressed.x; //Offset from screen value
          int offsetYStored = ptMsReleased.y - ptMsPressed.y;
          offsetXStored = (int) Math.round( offsetXStored / Sys.prjInfo.dZoomFactor);//Offset for stored value
          offsetYStored = (int) Math.round( offsetYStored / Sys.prjInfo.dZoomFactor);

          g = Sys.focusElem.getElemGeomObj();
          if( draggingStatus==DRAG_NODE_ONE ){
              g.x1Stored += offsetXStored;    g.y1Stored += offsetYStored;
          } else if( draggingStatus==DRAG_NODE_TWO){
              g.x2Stored += offsetXStored;    g.y2Stored += offsetYStored;
          } else{
              g.x1Stored += offsetXStored;    g.y1Stored += offsetYStored;
              g.x2Stored += offsetXStored;    g.y2Stored += offsetYStored;
          }
          g.setNode1FromStored( g.x1Stored, g.y1Stored);
          g.setNode2FromStored( g.x2Stored, g.y2Stored);
          if( g instanceof ThreeNodeElemGeom){
              ((ThreeNodeElemGeom)g).findNode3BasedonScreen(); }

          if( g instanceof LineGeom && Sys.focusElem.getAutoFlag(AbsElem.DATA_LENGTH)){//((LineGeom)g).isAutoValue == true) {
//              double len = (g.x1Stored - g.x2Stored)*(g.x1Stored - g.x2Stored);
//              len = len + (g.y1Stored - g.y2Stored)*(g.y1Stored - g.y2Stored);
//              len = Math.sqrt( len ); //get the length as stored value
//              Sys.focusElem.setData(AbsElem.DATA_LENGTH, len * Sys.prjInfo.lengthPerUnit); //per mile
              ((Line)Sys.focusElem).setLengthToAutoValue();
          }

          if( g.x1 == g.x2 && g.y1==g.y2){ //LengthOfElem =0
              mainFrame.menuBar.pushIntoQueue( EditAction.DELETE, vMovedList);
              String msg;
              if(Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER){
                  Sys.deleteElem( Sys.focusElem);
                  msg = "Image removed because length=0";
              }
              else if(Sys.prjInfo.iActiveLayer == Sys.prjInfo.BG_LINE_LAYER){
                  Sys.deleteElem( Sys.focusElem);
                  msg = "Background Line removed because LengthOfElem=0";
              }else{
                  Sys.clearConn( Sys.focusElem);
                  Sys.deleteElem( Sys.focusElem );
                  msg = "Element removed because LengthOfElem=0";
              }
              JOptionPane.showMessageDialog(mainFrame, msg );
          }
          else { //lengthOfElem > 0
              mainFrame.menuBar.pushIntoQueue( EditAction.MOVE, vMovedList);
              if( Sys.prjInfo.iActiveLayer > Sys.prjInfo.BG_LINE_LAYER ){
                  Sys.focusElem.getElemGeomObj().calcFrame();
                  Sys.clearConn( Sys.focusElem);
                  Sys.updateConn(Sys.focusElem);
              }
          }

          needRedraw=true;
          repaint();
      }
  }//moveOneElem

  //moving many elements;
  //???selecting many elements is a special case of moving many elements
  //in which moving distance is 0 ???
  private void moveManyElem( int mouseEventId ){
      AbsElemGeom g;
      Graphics graphics = this.getGraphics();

      if( mouseEventId == MouseEvent.MOUSE_PRESSED ){
          //should keep current selection
          Sys.addElemToSelection( Sys.focusElem);
          addToScreenImage( Sys.selectedList );
          repaint();

          if(Sys.prjInfo.freeze){  return;  } //is freezed?
      }
      else if( mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          if(Sys.prjInfo.freeze){ return;} //is freezed?
          graphics.setXORMode( Color.blue);
          int offsetX, offsetY;
          if( ptMsDraggedPrev.x ==0 && ptMsDraggedPrev.y == 0){// first mouseDrag event
              offsetX=ptMsDragged.x-ptMsPressed.x;
              offsetY=ptMsDragged.y-ptMsPressed.y;
          }
          else{
              offsetX=ptMsDragged.x-ptMsDraggedPrev.x;
              offsetY=ptMsDragged.y-ptMsDraggedPrev.y;
          }

          for ( int i=0; i<Sys.selectedList.size();i++){
              AbsElem e = (AbsElem) Sys.selectedList.elementAt(i);
              if( ptMsDraggedPrev.x !=0 || ptMsDraggedPrev.y != 0){
                  e.drawForMove( graphics ); //Not the first movement during dragging
              }
              g = e.getElemGeomObj();
              g.x1 += offsetX;
              g.y1 += offsetY;
              g.x2 += offsetX;
              g.y2 += offsetY;
              if( g instanceof ThreeNodeElemGeom){
                  ((ThreeNodeElemGeom)g).x3 += offsetX;
                  ((ThreeNodeElemGeom)g).y3 += offsetY;
              }
              g.calcFrame();
              e.drawForMove( graphics ); //!!!XOR
          }//end of for()
          graphics.setPaintMode();
      }
      else if( mouseEventId == MouseEvent.MOUSE_RELEASED ){
          if(Sys.prjInfo.freeze /*|| draggingStatus==DRAG_NONE*/ ){ return;} //is freezed?

          //no move or just a mouse click
          if( ptMsReleased.x==ptMsPressed.x && ptMsReleased.y==ptMsPressed.y){
              if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER ){
                  needRedraw = true;  //becoz BgImg may cover foreground elems
                  repaint();
              }
              return;
          }

          SysConst.progress=0;
          mainFrame.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
          UpdateProgressThread t= new UpdateProgressThread(mainFrame.btnPanel,2);//2=CONN
          t.start();

          //Must appear before sync stored(X,Y)
          Vector vMovedList=ElemForUndoRedo.createEfurForVector(Sys.selectedList);

          //Changing stored (x,y)
          int offsetXStored = ptMsReleased.x - ptMsPressed.x; //Offset from screen value
          int offsetYStored = ptMsReleased.y - ptMsPressed.y;
          offsetXStored = (int) Math.round( offsetXStored / Sys.prjInfo.dZoomFactor);//Offset for stored value
          offsetYStored = (int) Math.round( offsetYStored / Sys.prjInfo.dZoomFactor);

          int sz = Sys.selectedList.size();
          for(int i=0;i<sz;i++){
              AbsElem e=(AbsElem)Sys.selectedList.elementAt(i);
              g = e.getElemGeomObj();
              g.x1Stored += offsetXStored;    g.y1Stored += offsetYStored;
              g.x2Stored += offsetXStored;    g.y2Stored += offsetYStored;

              //set screen (XY) for node 1 and node 2
              g.setNode1FromStored( g.x1Stored, g.y1Stored);
              g.setNode2FromStored( g.x2Stored, g.y2Stored);
              if( g instanceof ThreeNodeElemGeom){
                  ((ThreeNodeElemGeom)g).findNode3BasedonScreen(); }

              if( Sys.prjInfo.iActiveLayer > Sys.prjInfo.BG_LINE_LAYER ){
                  g.calcFrame(); // recalc frame
                  Sys.clearConn( e);
                  Sys.updateConn(e);
              }
              SysConst.progress = (int) (19.0 * i / sz );
          }
          SysConst.progress = 20; //MAX

          mainFrame.menuBar.pushIntoQueue( EditAction.MOVE, vMovedList);
          needRedraw=true;
          repaint();
          mainFrame.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
      //needRedraw=true;
      //repaint();
  }//moveManyElem()

  private void zoomIntoWindow( int mouseEventId){
      Graphics g =getGraphics();

      if( mouseEventId == MouseEvent.MOUSE_PRESSED){
          //do nothing; don't clear selection during zooming
      }
      else if( mouseEventId == MouseEvent.MOUSE_DRAGGED){
          g.setXORMode(Color.yellow);
          Sys.draggedRect = findSelectedRect(ptMsPressed, ptMsDragged);

          Rectangle prevRect;
          if( ptMsDraggedPrev.x!=0){
              prevRect = findSelectedRect(ptMsPressed, ptMsDraggedPrev);
              g.drawRect(prevRect.x-1, prevRect.y-1, prevRect.width+2, prevRect.height+2);
              g.drawRect(prevRect.x, prevRect.y, prevRect.width, prevRect.height);
              g.drawRect(prevRect.x+1, prevRect.y+1, prevRect.width-2, prevRect.height-2);
              //draw 3 rectangles actually; sort of bold.
          }

          g.drawRect( Sys.draggedRect.x-1, Sys.draggedRect.y-1,
                       Sys.draggedRect.width+2, Sys.draggedRect.height+2);
          g.drawRect( Sys.draggedRect.x, Sys.draggedRect.y,
                       Sys.draggedRect.width, Sys.draggedRect.height);
          g.drawRect( Sys.draggedRect.x+1, Sys.draggedRect.y+1,
                       Sys.draggedRect.width-2, Sys.draggedRect.height-2);
          g.setPaintMode();
      }
      else if( mouseEventId == MouseEvent.MOUSE_RELEASED){
          // ptMsReleased equals to ptMsDragged (last one)
          Sys.draggedRect = findSelectedRect(ptMsPressed, ptMsReleased);
          if( Sys.draggedRect.width < 4 &&  Sys.draggedRect.height< 4 ){
              JOptionPane.showMessageDialog(mainFrame, "Selected Area Too Small!");
              Sys.draggedRect.setBounds(0,0,0,0);
              repaint();
              return;  // rectangle too small
          }

          zoomIntoRect( Sys.draggedRect );

          //reset the buttons at ButtonPanel btnPanel
          mainFrame.btnPanel.unselectAllButtons();
          mainFrame.btnPanel.selectButton( mainFrame.btnPanel.BTN_ARROW);

          //reset Sys.draggedRect
          Sys.draggedRect.setBounds(0,0,0,0);
      }
  }// end of zoomintoWindow()


  private void zoomIntoRect( Rectangle rect){
      int x=mainFrame.scrollPane.getViewport().getViewPosition().x;
      int y=mainFrame.scrollPane.getViewport().getViewPosition().y;
      Sys.setPrevViewPosition( x,y); // for zoom_prev

      Sys.prjInfo.setZoomFactor( findScaleForRect( rect ) ); //set new SF
      Point pt = locateViewPosition( rect );  // locate the new leftTop ViewPosition
      updateViewSize( pt );
      mainFrame.scrollPane.getViewport().setViewPosition( pt);

      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect();
      Sys.updateAllCoord( newRect );

      needRedraw = true;
      repaint();
  }//end of zoomIntoRect( )

  private void doCommonToAddElemByOneClick(AbsElem elem){
      elem.setData(AbsElem.DATA_LAYER, Sys.prjInfo.iActiveLayer);
      elem.setCustomizedFlag(AbsElem.DATA_LAYER, true);
      unselectAndUpdateScreen();
      Sys.focusElem = elem;

      Vector v=new Vector();
      v.add(elem);  //ADD

      if( elem.getElemGeomObj() instanceof TwoNodeElemGeom){
          Line theLine = (Line) canSmartAdd(elem); //adjustIfLineExisted( elem); //auto line-breaking
          if( theLine == null){ //if true, conn already updated
              Sys.addElem(elem, true, true);
              addToScreenImage( elem );
              needRedraw = false;
              mainFrame.menuBar.pushIntoQueue( EditAction.ADD, v );
          }
          else{
              v.add( new ElemForUndoRedo(theLine) ); //MOVE
              doSmartAdd( elem, theLine ); //theLine moved
              Sys.addElem(elem, true, true);
              Sys.clearConn(theLine);
              Sys.updateConn(theLine);
              needRedraw = true;
              mainFrame.menuBar.pushIntoQueue( EditAction.SMART_ADD, v );
          }
          repaint();
          //no need to update max & min
      } else {
          Sys.addElem( elem, true, true );
          addToScreenImage( elem );
          repaint();
          mainFrame.menuBar.pushIntoQueue( EditAction.ADD, v );
      }

      ButtonPanel bp = mainFrame.btnPanel;
      if( bp.btnArray[bp.BTN_LOCK].isSelected() == false) {
          mainFrame.btnPanel.selectButton( bp.BTN_ARROW);
      }
  }

  private void addSource( ){
      String s="Source_" + ( Sys.elemList.size()+1 ); //External Id
      Source theSource=new Source();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theSource.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      geom.incRotate(); geom.incRotate();  //to South
      theSource.setDftTemp(Source.objActiveTemp);
      if( Source.objActiveTemp != null ) {
          Templates.copyAllDataFromTemp( theSource, Source.objActiveTemp ); }

      doCommonToAddElemByOneClick(theSource);
  }

  private void addDG( ){
      String s="Distributed_Generation_" + ( Sys.elemList.size()+1 ); //External Id
      DG theDG=new DG();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theDG.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theDG.setDftTemp(DG.objActiveTemp);
      Templates.copyAllDataFromTemp(theDG, DG.objActiveTemp);
      doCommonToAddElemByOneClick(theDG);
  }

  private void addTxfm( ){
      String s="Transformer_" + ( Sys.elemList.size()+1 ); //External Id
      Txfm theTxfm=new Txfm();
      TwoNodeElemGeom geom = new TwoNodeElemGeom();
      theTxfm.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theTxfm.setDftTemp(Txfm.objActiveTemp);
      Templates.copyAllDataFromTemp(theTxfm, Txfm.objActiveTemp);

      doCommonToAddElemByOneClick(theTxfm);
  }

  private void addTxfm3( ){
      JOptionPane.showMessageDialog(mainFrame,
          "Three-winding transformer is not available in this version.");

      ButtonPanel bp = mainFrame.btnPanel;
      if( bp.btnArray[bp.BTN_LOCK].isSelected() == false) {
          mainFrame.btnPanel.selectButton( bp.BTN_ARROW);
      }
  }

  private void addReg( ){
      String s="Regulator_" + ( Sys.elemList.size()+1 ); //External Id
      Regulator theRegulator=new Regulator();
      TwoNodeElemGeom geom = new TwoNodeElemGeom();
      theRegulator.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theRegulator.setDftTemp(Regulator.objActiveTemp);
      Templates.copyAllDataFromTemp(theRegulator, Regulator.objActiveTemp);

      doCommonToAddElemByOneClick(theRegulator);
  }

  private void addBreaker( ){
      String s="Breaker_" + ( Sys.elemList.size()+1 ); //External Id
      Breaker theBreaker=new Breaker();
      SwtGeom geom = new SwtGeom();
      theBreaker.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theBreaker.setDftTemp(Breaker.objActiveTemp);
      Templates.copyAllDataFromTemp(theBreaker, Breaker.objActiveTemp);

      doCommonToAddElemByOneClick(theBreaker);
  }

  private void addDrtBreaker( ){
      String s="Drawout_Breaker_" + ( Sys.elemList.size()+1 ); //External Id
      DrtBreaker theDrtBreaker=new DrtBreaker();
      SwtGeom geom = new SwtGeom();
      theDrtBreaker.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theDrtBreaker.setDftTemp(DrtBreaker.objActiveTemp);
      Templates.copyAllDataFromTemp(theDrtBreaker, DrtBreaker.objActiveTemp);

      doCommonToAddElemByOneClick(theDrtBreaker);
  }

  private void addRecloser( ){
      String s="Recloser_" + ( Sys.elemList.size()+1 ); //External Id
      Recloser theRecloser=new Recloser();
      SwtGeom geom = new SwtGeom();
      theRecloser.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theRecloser.setDftTemp(Recloser.objActiveTemp);
      Templates.copyAllDataFromTemp(theRecloser, Recloser.objActiveTemp);
      doCommonToAddElemByOneClick( theRecloser);
  }

  private void addSwitch( ){
      String s="Switch_" + ( Sys.elemList.size()+1 ); //External Id
      Switch theSwitch=new Switch();
      SwtGeom geom = new SwtGeom();
      theSwitch.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theSwitch.setDftTemp(Switch.objActiveTemp);
      Templates.copyAllDataFromTemp(theSwitch, Switch.objActiveTemp);

      doCommonToAddElemByOneClick(theSwitch);
  }

  private void addSwitch3( ){
      String s="Transfer_Switch_" + ( Sys.elemList.size()+1 ); //External Id
      Switch3 theSwitch3=new Switch3();
      ThreeNodeElemGeom geom = new ThreeNodeElemGeom();
      theSwitch3.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theSwitch3.setDftTemp(Switch3.objActiveTemp);
      Templates.copyAllDataFromTemp(theSwitch3, Switch3.objActiveTemp);

      doCommonToAddElemByOneClick(theSwitch3);
  }


  private void addSwitchGnd( ){
      String s="Grounding_Switch_" + ( Sys.elemList.size()+1 ); //External Id
      SwitchGnd theSwitchGnd=new SwitchGnd();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theSwitchGnd.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theSwitchGnd.setDftTemp(SwitchGnd.objActiveTemp);
      Templates.copyAllDataFromTemp(theSwitchGnd, SwitchGnd.objActiveTemp);

      doCommonToAddElemByOneClick(theSwitchGnd);
  }

  private void addFuse( ){
      String s="Fuse_" + ( Sys.elemList.size()+1 ); //External Id
      Fuse theFuse=new Fuse();
      SwtGeom geom = new SwtGeom();
      theFuse.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theFuse.setDftTemp(Fuse.objActiveTemp);
      Templates.copyAllDataFromTemp(theFuse, Fuse.objActiveTemp);

      doCommonToAddElemByOneClick(theFuse);
  }

  private void addSect( ){
      String s="Sectionalizer_" + ( Sys.elemList.size()+1 ); //External Id
      Sect theSect=new Sect();
      SwtGeom geom = new SwtGeom();
      theSect.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theSect.setDftTemp(Sect.objActiveTemp);
      Templates.copyAllDataFromTemp(theSect, Sect.objActiveTemp);

      doCommonToAddElemByOneClick(theSect);
  }


  private void addReactor( ){
      String s="Reactor_" + ( Sys.elemList.size()+1 ); //External Id
      Reactor theReactor=new Reactor();
      TwoNodeElemGeom geom = new TwoNodeElemGeom();
      theReactor.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theReactor.setDftTemp(Reactor.objActiveTemp);
      Templates.copyAllDataFromTemp(theReactor, Reactor.objActiveTemp);

      doCommonToAddElemByOneClick(theReactor);
  }

  private void addLoad( ){
      String s="Load_" + ( Sys.elemList.size()+1 ); //External Id
      Load theLoad=new Load();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theLoad.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theLoad.setDftTemp(Load.objActiveTemp);
      Templates.copyAllDataFromTemp(theLoad, Load.objActiveTemp);

      doCommonToAddElemByOneClick(theLoad);
  }

  private void addSeriesCap( ){
      String s="Series_Capacitor_" + ( Sys.elemList.size()+1 ); //External Id
      SeriesCap theSeriesCap=new SeriesCap();
      TwoNodeElemGeom geom = new TwoNodeElemGeom(  );
      theSeriesCap.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theSeriesCap.setDftTemp(SeriesCap.objActiveTemp);
      Templates.copyAllDataFromTemp(theSeriesCap, SeriesCap.objActiveTemp);

      doCommonToAddElemByOneClick(theSeriesCap);
  }

  private void addCap( ){
      String s="Capacitor_" + ( Sys.elemList.size()+1 ); //External Id
      Cap theCap=new Cap();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theCap.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theCap.setDftTemp(Cap.objActiveTemp);
      Templates.copyAllDataFromTemp(theCap, Cap.objActiveTemp);

      doCommonToAddElemByOneClick(theCap);
  }

  private void addCT( ){
      String s="Current_Transformer_" + ( Sys.elemList.size()+1 ); //External Id
      CT theCT=new CT();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theCT.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theCT.setDftTemp(CT.objActiveTemp);
      Templates.copyAllDataFromTemp(theCT, CT.objActiveTemp);

      doCommonToAddElemByOneClick(theCT);
  }

  private void addMotor( ){
      String s="Motor_" + ( Sys.elemList.size()+1 ); //External Id
      Motor theMotor=new Motor();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      theMotor.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theMotor.setDftTemp(Motor.objActiveTemp);
      Templates.copyAllDataFromTemp(theMotor, Motor.objActiveTemp);

      doCommonToAddElemByOneClick(theMotor);
  }

  private void addArrester( ){
      String s="Surge_Arrester_"+ ( Sys.elemList.size()+1 ); //External Id
      Arrester theArrester=new Arrester();
      OneNodeElemGeom geom = new OneNodeElemGeom( );
      theArrester.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      theArrester.setDftTemp(Arrester.objActiveTemp);
      Templates.copyAllDataFromTemp(theArrester, Arrester.objActiveTemp);

      doCommonToAddElemByOneClick(theArrester);
  }

  private void addPT( ){
      String s="Voltage_Transformer_" + ( Sys.elemList.size()+1 ); //External Id
      PT thePT=new PT();
      OneNodeElemGeom geom = new OneNodeElemGeom();
      thePT.setElemGeomObj(geom);
      geom.initFromScreen(s, ptMsPressed);
      thePT.setDftTemp(PT.objActiveTemp);
      Templates.copyAllDataFromTemp(thePT, PT.objActiveTemp);

      doCommonToAddElemByOneClick(thePT);
  }

  private void addLineOrCable(  int mouseEventId){
      ButtonPanel bp = mainFrame.btnPanel;
      Graphics g = this.getGraphics(); // this == drawing panel

      if( mouseEventId == MouseEvent.MOUSE_PRESSED){
          unselectAndUpdateScreen();
          Sys.focusElem = null;
          repaint();
      }
      else if( mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          g.setXORMode(Color.blue);
          if( ptMsDraggedPrev.x!=0){ //not the first drag; need to be XORed
              g.drawLine(ptMsPressed.x,ptMsPressed.y, ptMsDraggedPrev.x, ptMsDraggedPrev.y);
          }
          g.drawLine(ptMsPressed.x,ptMsPressed.y, ptMsDragged.x, ptMsDragged.y);
          ptMsDraggedPrev.setLocation( ptMsDragged);
          g.setPaintMode();
      }
      else if( mouseEventId == MouseEvent.MOUSE_RELEASED ){ // record End Coord
          Line theLine=new Line();
          LineGeom gLine = new LineGeom( );
          theLine.setElemGeomObj(gLine);
          if( bp.idOfDrawingBtn == bp.BTN_CABLE ){
              String s="Cable_" + ( Sys.elemList.size()+1 ); //External Id
              gLine.initFromScreen(s, true);
              theLine.setDftTemp(Line.objActiveTemp2);
              Templates.copyAllDataFromTemp(theLine, Line.objActiveTemp2);
          } else{
              String s="Line_" + ( Sys.elemList.size()+1 ); //External Id
              gLine.initFromScreen(s, false);
              theLine.setDftTemp(Line.objActiveTemp);
              Templates.copyAllDataFromTemp(theLine, Line.objActiveTemp);
          }
          theLine.setData(AbsElem.DATA_LAYER, Sys.prjInfo.iActiveLayer);
          theLine.setCustomizedFlag(AbsElem.DATA_LAYER, true);
          gLine.setNode1FromScreen( ptMsPressed );
          gLine.setNode2FromScreen( ptMsReleased);

          if( gLine.x1Stored == gLine.x2Stored  && gLine.y1Stored==gLine.y2Stored){ //LengthOfLine ==0;
              JOptionPane.showMessageDialog(mainFrame, "No Line Added because LengthOfLine=0");
          }
          else{
              Sys.addElem( theLine, true, true);
              Sys.focusElem = theLine;
              mainFrame.menuBar.pushIntoQueue( EditAction.ADD, theLine );

//              double len = (gLine.x1Stored - gLine.x2Stored)*(gLine.x1Stored - gLine.x2Stored);
//              len = len + (gLine.y1Stored - gLine.y2Stored)*(gLine.y1Stored - gLine.y2Stored);
//              len = Math.sqrt( len ); //get the length as stored value
//              theLine.setData(AbsElem.DATA_LENGTH, len * Sys.prjInfo.lengthPerUnit); // in current unit
              theLine.setLengthToAutoValue();
              theLine.setAutoFlag( AbsElem.DATA_LENGTH, true); //geom.isAutoValue =true;

              addToScreenImage(theLine);  //update image
              repaint();
          }

          if( bp.btnArray[bp.BTN_LOCK].isSelected() == false) {
              mainFrame.btnPanel.selectButton( bp.BTN_ARROW);
          }
      }
  }

  private void addBus( int mouseEventId){
      ButtonPanel bp = mainFrame.btnPanel;
      Graphics g = this.getGraphics(); // this == drawing panel

      if( mouseEventId == MouseEvent.MOUSE_PRESSED){
          unselectAndUpdateScreen();
          Sys.focusElem = null;
          repaint();
      } else if( mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          g.setXORMode(Color.blue);
          if( ptMsDraggedPrev.x!=0){ //not the first drag; need to be XORed
              g.drawLine(ptMsPressed.x,ptMsPressed.y, ptMsDraggedPrev.x, ptMsDraggedPrev.y);
          }
          g.drawLine(ptMsPressed.x,ptMsPressed.y, ptMsDragged.x, ptMsDragged.y);
          ptMsDraggedPrev.setLocation( ptMsDragged);
          g.setPaintMode();
      } else if( mouseEventId == MouseEvent.MOUSE_RELEASED ){ // record End Coord
          String s="Bus_" + ( Sys.elemList.size()+1 ); //External Id
          Bus theBus=new Bus();
          BusGeom gBus = new BusGeom();
          theBus.setElemGeomObj( gBus );
          gBus.initFromScreen(s);
          gBus.setNode1FromScreen( ptMsPressed );
          gBus.setNode2FromScreen( ptMsReleased );

          theBus.setData(AbsElem.DATA_LAYER, Sys.prjInfo.iActiveLayer);
          theBus.setCustomizedFlag(AbsElem.DATA_LAYER, true);
          theBus.setDftTemp(Bus.objActiveTemp);
          Templates.copyAllDataFromTemp(theBus, Bus.objActiveTemp);

          if( gBus.x1Stored==gBus.x2Stored  && gBus.y1Stored==gBus.y2Stored){ //LengthOfLine ==0;
              JOptionPane.showMessageDialog(mainFrame, "No Bus Added because LengthOfLine=0");
          } else{
              Sys.addElem( theBus, true, true );
              Sys.focusElem = theBus;
              mainFrame.menuBar.pushIntoQueue( EditAction.ADD, theBus );
              addToScreenImage(theBus);  //update image
              repaint();
          }

          if( bp.btnArray[bp.BTN_LOCK].isSelected() == false) {
              mainFrame.btnPanel.selectButton( bp.BTN_ARROW);
          }
      }
  } // end of addBus();

  public void addBgImage( String fileName ) {
      BgImage bgImage = new BgImage( fileName );
      int x = mainFrame.mainPanel.INIT_VIEW_WIDTH  / 2 ;
      int y = mainFrame.mainPanel.INIT_VIEW_HEIGHT / 2;
      int w = (int) ( Sys.prjInfo.dZoomFactor * bgImage.getImage().getWidth( mainFrame ) );
      int h = (int) ( Sys.prjInfo.dZoomFactor * bgImage.getImage().getHeight( mainFrame ));
      bgImage.initFromScreen(x, y, x+w, y+h );
      Sys.imgList.add( bgImage );
      mainFrame.menuBar.zoomAll();

      mainFrame.menuBar.pushIntoQueue( EditAction.ADD, bgImage );
  }//end of addBgImage

  private void addBgLine( int mouseEventId){
      ButtonPanel bp = mainFrame.btnPanel;
      BgLine bgLine;
      Graphics g = this.getGraphics(); // this == drawing panel

      if( mouseEventId == MouseEvent.MOUSE_PRESSED){
          unselectAndUpdateScreen();
          Sys.focusElem = null;
          repaint();
      }
      else if( mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          g.setXORMode(Color.blue);
          if( ptMsDraggedPrev.x!=0){ //not the first drag; need to be XORed
              g.drawLine(ptMsPressed.x,ptMsPressed.y, ptMsDraggedPrev.x, ptMsDraggedPrev.y);
          }
          g.drawLine(ptMsPressed.x,ptMsPressed.y, ptMsDragged.x, ptMsDragged.y);
          ptMsDraggedPrev.setLocation( ptMsDragged);
          g.setPaintMode();
      }
      else if( mouseEventId == MouseEvent.MOUSE_RELEASED ){ // record End Coord
          bgLine = (BgLine) BgLine.dftBgLine.clone();
          LineGeom bgLineGeom = (LineGeom) bgLine.getElemGeomObj();
          //bgLine.setElemGeomObj( bgLineGeom );
          bgLine.setData( AbsElem.DATA_LAYER, Sys.prjInfo.BG_LINE_LAYER );
          bgLineGeom.setNode1FromScreen( ptMsPressed );
          bgLineGeom.setNode2FromScreen( ptMsReleased);

          if( bgLineGeom.x1Stored == bgLineGeom.x2Stored &&
              bgLineGeom.y1Stored == bgLineGeom.y2Stored){
                  JOptionPane.showMessageDialog(mainFrame,
                      "No Background Line Added because LengthOfLine=0");
          }else{
              Sys.bgElemList.add( bgLine );
              //Sys.screenElemList.add( bgLine ); //Not for BgLine
              Sys.addElemToSelection( bgLine );
              Sys.focusElem = bgLine;

              mainFrame.menuBar.pushIntoQueue( EditAction.ADD, bgLine );

              addToScreenImage( bgLine );  //update image
              repaint();
          }

          if( bp.btnArray[bp.BTN_LOCK].isSelected() == false) {
              mainFrame.btnPanel.selectButton( bp.BTN_ARROW);
          }
      }
  } // end of addBgLine();

  private void addGroup( int mouseEventId){
      ButtonPanel bp = mainFrame.btnPanel;
      Graphics g = this.getGraphics();

      if( mouseEventId == MouseEvent.MOUSE_PRESSED){
          unselectAndUpdateScreen();
          Sys.focusElem = null;
          repaint();
      }
      else if( mouseEventId == MouseEvent.MOUSE_DRAGGED ){
          g.setXORMode(Color.blue);
          Rectangle rect;
          if( ptMsDraggedPrev.x!=0){ //not the first drag; need to be XORed
              rect = findSelectedRect( ptMsPressed, ptMsDraggedPrev);
              g.drawRect(rect.x, rect.y, rect.width, rect.height);
          }
          rect = findSelectedRect( ptMsPressed, ptMsDragged);
          g.drawRect(rect.x, rect.y, rect.width, rect.height);
          ptMsDraggedPrev.setLocation( ptMsDragged);
          g.setPaintMode();
      }
      else if( mouseEventId == MouseEvent.MOUSE_RELEASED ){ // record End Coord
          String s="Group_" + ( Sys.elemList.size()+1 ); //External Id
          Group theGroup=new Group();
          GroupGeom gGroup = new GroupGeom();
          theGroup.setElemGeomObj( gGroup );
          gGroup.initFromScreen(s);
          gGroup.setNode1FromScreen( ptMsPressed );
          gGroup.setNode2FromScreen( ptMsReleased );

          theGroup.setData(AbsElem.DATA_LAYER, Sys.prjInfo.iActiveLayer);
          theGroup.setCustomizedFlag(AbsElem.DATA_LAYER, true);
          theGroup.setDftTemp(Group.objActiveTemp);
          Templates.copyAllDataFromTemp(theGroup, Group.objActiveTemp);

          if( gGroup.x1Stored == gGroup.x2Stored  && gGroup.y1Stored==gGroup.y2Stored){ //LengthOfLine ==0;
              JOptionPane.showMessageDialog(mainFrame, "No Group Added because LengthOfLine=0");
          } else{
              gGroup.resetNode1AndNode2();
              gGroup.calcFrame();

              Sys.addElem( theGroup, true, true );
              Sys.focusElem = theGroup;
              mainFrame.menuBar.pushIntoQueue( EditAction.ADD, theGroup );
              addToScreenImage( theGroup );  //update image
              repaint();
          }

          if( bp.btnArray[bp.BTN_LOCK].isSelected() == false) {
              mainFrame.btnPanel.selectButton( bp.BTN_ARROW);
          }
      }//end of MOUSE_RELEASED
  } // end of addGroup();


  /*===========================================================
  Utility methods called by High-level event processing methods
      public  void      paintComponent(Graphics g)
      private Rectangle findClipRect()
      private Rectangle findSelectedRect()
      private float     findScaleForRect  ( Rectangle r )
      private Point     locateViewPosition( Rectangle r )
  ============================================================*/

  public void paintComponent(Graphics g){
      super.paintComponent( g );

      if(SysConst.DEBUG_MODE >= 1){
          //System.out.println("Enter repaint() : "+ (cntrRepaint++) );
      }
      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect();

      //update the screenElemList
      if( bZoomAll == true ){
          Sys.screenElemList.removeAllElements();
          Sys.screenElemList.addAll( Sys.elemList );
          bZoomAll = false;
      } else if( newRect.equals(prevRect)==false){
          //if zoomed(dZoomFactor changed) or resized(rect changed) or viewport scrolled
          //then re-compute all elem in the current viewport
          Sys.updateScreenList( newRect );
          prevRect.x= newRect.x;
          prevRect.y= newRect.y;
          prevRect.width= newRect.width;
          prevRect.height= newRect.height;
          needRedraw = true;
      }

      if( needRedraw ){
          redrawScreenImage();
          needRedraw=false;
      }
      g.drawImage(image, newRect.x, newRect.y, null);

      //Gridlines are drawn in XOR mode
      if( Sys.prjInfo.showGridLine ){ paintGridLineInXorMode(g, 0, 0);}
      if( Sys.prjInfo.drawText ){ paintText( g ); }
      if( Sys.focusElem!=null) { Sys.focusElem.drawFrame( g ); }
      if( Sys.prjInfo.showNodes ){ paintNodes(g); }
      if( Sys.prjInfo.hasAnyHighLight() || Sys.highLightedElemList.size()>0 ){
          this.paintHighLighted( g ); }

      Point pt = new Point( this.mainFrame.scrollPane.getViewport().getViewPosition() );;
      if( Sys.prjInfo.showLegend ){
          paintShadingLegend( g , pt );   //rectShadingBox will be updated
          pt.setLocation( rectShadingBox.x,
                          rectShadingBox.y + rectShadingBox.height);
      } else {
          rectShadingBox.setBounds(pt.x, pt.y, 0, 0);
      }

      if( Sys.prjInfo.showScale ){
          paintScale(g, pt );   //rectShadingBox will be updated
          pt.setLocation( rectScaleBox.x,
                          rectScaleBox.y + rectScaleBox.height);
      } else{
          rectScaleBox.setBounds(pt.x, pt.y, 0, 0);
      }

      mainFrame.statusBar.updateText();
  }//end of paintComponent();

  public void setNeedRedraw(boolean b){ needRedraw=b;}

  //this will be called when needRedraw==true
  private void redrawScreenImage(){
      if(SysConst.DEBUG_MODE >= 1){
          //System.out.println("Redraw everything");
      }
      AbsElem theElem;
      AbsElemGeom theGeom;

      Graphics g = image.getGraphics(); //image.getGraphics();
      Color oldColor = g.getColor();
      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect();
      g.setColor( Color.white); //new Color(204,204,204));//Color.gray);
      g.fillRect(0,0,newRect.width,newRect.height);//newRect.width,newRect.height);

      int offsetX = newRect.x;
      int offsetY = newRect.y;

      //redraw all images;
      //practically, # of images should <= 100
      for(int i=0; i<Sys.imgList.size(); i++){
          theElem =( AbsElem ) Sys.imgList.elementAt(i);
          theElem.draw(g, offsetX, offsetY);
      }

      //redraw all back ground elements;
      //practically, # of bg elements should <= 100
      for(int i=0; i<Sys.bgElemList.size(); i++){
          theElem =( AbsElem ) Sys.bgElemList.get(i);
          theElem.draw(g, offsetX, offsetY);
      }

      //redraw screenElem only
      for(int i=0; i<Sys.screenElemList.size(); i++){
          theElem=(AbsElem) Sys.screenElemList.get(i);
          theElem.draw(g, offsetX, offsetY);
      }

      g.setColor(oldColor);
  }

  void unselectAndUpdateScreen() {
      if( Sys.selectedList.size() > 0 ) {
          Vector vPrevSelected = (Vector) Sys.selectedList.clone();
          Sys.clearSelection(); //But KEEP the FOCUS ELEM
          addToScreenImage( vPrevSelected);
      }
  }

  private void addToScreenImage( Vector v){
      AbsElem elem;
      for(int i=0; i<v.size(); i++){
          elem = (AbsElem) v.get(i);
          addToScreenImage(elem);
      }
  }

  private void addToScreenImage(AbsElem elem){
      Graphics g = image.getGraphics();
      Color oldColor = g.getColor();
      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect();
      elem.draw(g, newRect.x, newRect.y);
      g.setColor( oldColor);
  }
//
//  private void eraseFromScreen(AbsElem elem){
//      if( image == null) {
//          Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//          image= createImage( dim.width, dim.height );
//      }
//      Graphics g = image.getGraphics();
//      //Color oldColor = g.getColor();
//      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect();
//      //g.setXORMode( Color.yellow);
//      //elem.draw(g, newRect.x, newRect.y);
//      //g.setPaintMode();
//      elem.draw(g, newRect.x, newRect.y);
//      //g.setColor( oldColor);
//  }

  private Rectangle findSelectedRect(Point ptStart, Point ptEnd){
      //to compare 2 Point: ptMsDragged & ptMsPressed
      int xLeftTop, yLeftTop;
      int w = ptEnd.x - ptStart.x;
      int h = ptEnd.y - ptStart.y;

      if( w >0 && h > 0 )      { xLeftTop=ptStart.x; yLeftTop=ptStart.y; }
      else if( w >0 && h <= 0 ){ xLeftTop=ptStart.x; yLeftTop=ptEnd.y;  }
      else if( w <=0 && h > 0 ){ xLeftTop=ptEnd.x;   yLeftTop=ptStart.y; }
      else                     { xLeftTop=ptEnd.x;   yLeftTop=ptEnd.y; }
      w=Math.abs(w); h=Math.abs(h);

      return new Rectangle ( xLeftTop, yLeftTop, w, h );
  }

  private float findScaleForRect ( Rectangle r ){
      //find the width & height of ViewPort
      int vpWidth  = mainFrame.scrollPane.getViewportBorderBounds().width;
      int vpHeight = mainFrame.scrollPane.getViewportBorderBounds().height;

      //a, b are the S.F. relative to Viewport size
      float a = ((float) vpWidth )/((float)r.width);
      float b = ((float) vpHeight)/((float)r.height);

      a=Math.min(a,b); // a = S.F. Relative to current Viewport size
      a *= Sys.prjInfo.dZoomFactor;  // Absolute S.F. = a *  Current S.F.

      return a;  // new S.F. (absolute)
  }

  //access: default (Package)
  Point locateViewPosition( Rectangle r){
      Point pt =new Point();

      pt.x = r.x;
      pt.y = r.y;//yLeftTop;  // pt--> LeftTop of the selected area

      pt.x += r.width/2;
      pt.y += r.height/2; //pt--> center of ViewPort

      pt.x = (int) ( ((float)pt.x) * Sys.prjInfo.dZoomFactor / Sys.prjInfo.dPrevZoomFactor);
      pt.y = (int) ( ((float)pt.y) * Sys.prjInfo.dZoomFactor / Sys.prjInfo.dPrevZoomFactor);
      //pt --> center of Viewport after scaled

      pt.x -= mainFrame.scrollPane.getViewportBorderBounds().width/2;
      pt.y -= mainFrame.scrollPane.getViewportBorderBounds().height/2;
      // pt--> topleft corner of ViewPort after sacled

      if(pt.x<0) pt.x = 0;
      if(pt.y<0) pt.y = 0;

      return pt;
  }

  //access: default (Package)
  // leftTop is the new left-top point of the Viewport in the View based on new scale
  void updateViewSize( Point leftTop){
      int  tmp1=Math.max(INIT_VIEW_WIDTH ,(int)(Sys.prjInfo.dZoomFactor *Sys.SYS_MAX_X));  //INIT_VIEW_* is the min size
      int tmp2=Math.max(INIT_VIEW_HEIGHT,(int)(Sys.prjInfo.dZoomFactor *Sys.SYS_MAX_Y));  //of View

      int vpWidth  = mainFrame.scrollPane.getViewportBorderBounds().width;
      int vpHeight = mainFrame.scrollPane.getViewportBorderBounds().height;

      tmp1= Math.max( tmp1, vpWidth + leftTop.x); // leftTop.x + vpWidth =rightBottom.x
                                                  // it's possible (tmp1,tmp2) < bottomRight
      tmp2= Math.max( tmp2, vpHeight+ leftTop.y); // bottomRight point of the screen must be
                                                  // covered at least

      mainFrame.scrollPane.getViewport().setViewSize(new Dimension(tmp1,tmp2));
      this.setPreferredSize(new Dimension(tmp1,tmp2));
      //this.revalidate();

  }


  //make Elem e located in the center of viewport
  //No change of ZoomFactor
  void centerForElem( AbsElem e){
      AbsElemGeom g = e.getElemGeomObj();
      Point pt = new Point(0,0);
      pt.x = (g.x1 + g.x2)/2 ;
      pt.y = (g.y1 + g.y2)/2 ;

      Rectangle rect = mainFrame.scrollPane.getViewportBorderBounds();

      pt.x = pt.x - (rect.width/2);
      pt.y = pt.y - (rect.height/2);

      pt.x = Math.max(0, pt.x);
      pt.y = Math.max(0, pt.y);

      mainFrame.mainPanel.updateViewSize( pt);
      mainFrame.scrollPane.getViewport().setViewPosition( pt );

      needRedraw = true;
      repaint();
  }//end of centerForElem()

  //make Elem e located in the center of viewport
  //ZoomFactor may change
  void centerForVector( Vector v){
      Rectangle rect = Sys.calcBoundRect( v );
      rect.x = (int) (rect.x * Sys.prjInfo.dZoomFactor);
      rect.y = (int) (rect.y * Sys.prjInfo.dZoomFactor);
      rect.width = (int) (rect.width * Sys.prjInfo.dZoomFactor );
      rect.height = (int) (rect.height * Sys.prjInfo.dZoomFactor );
      if( rect.width <=4  ) { rect.width  = 4; }  //min rect width
      if( rect.height <=4 ) { rect.height = 4; }  //min rect height

      this.zoomIntoRect( rect ); //it needs redraw
  }//end of centerForVector



  private void paintShadingLegend(Graphics g, Point topLeft){
      Color originalColor = g.getColor();
      Font  originalFont  = g.getFont();
      //Point bottomLeft= new Point( topLeft );//this.mainFrame.scrollPane.getViewport().getViewPosition() );

      int x=topLeft.x;
      int y=topLeft.y;
      int width = 150;  //fixed value for now.
      int height=0;       //height of the legend box
      int padding = 10; //Left, right, top, bottom,

      Font f = GuiConst.fontDefault;
      g.setFont(f);

      String title="";
      String[] strLegend;
      Color [] colorLegend;
      boolean continuous = false;


      if( Sys.prjInfo.shadingCat >= 0) {
          ShadingOption so = Sys.prjInfo.currentShadingOption;
          NumberFormat nf = NumberFormat.getNumberInstance();
          nf.setMaximumFractionDigits(4);
          nf.setGroupingUsed(false);

          height = 20+ 15*6; //6 slots if shaded by a data/result field
          title = so.getDescription();
          continuous = so.getContinuous();

          double start = so.getStartValue();
          double bin = so.getBinSize();

          strLegend = new String[6];
          colorLegend = SysConst.shadingColors;
          strLegend[0] = (bin > 0 ? "< " : "> ") + nf.format(start);
          if (continuous) {
              for (int i = 1; i < 5; i++)
                  strLegend[i] = nf.format(start + (i-0.5)*bin);
          } else {
              for (int i = 1; i < 5; i++)
                  strLegend[i] = nf.format(start + (i-1)*bin) + " - " +
                      nf.format(start + i*bin);
          }
          strLegend[5] = (bin > 0 ? ">= " : "<= ") + nf.format(start + 4*bin);
      }
      else if( Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_STUDY_AREA){
          height = 20 + 15*2;  // only two lines
          title = "Study Area";

          strLegend = new String[2];
          strLegend[0]="Not included";
          strLegend[1]="Included";

          colorLegend = new Color[2];
          colorLegend[0]=SysConst.shadingColors[1];
          colorLegend[1]=SysConst.shadingColors[2];
      }
      else if( Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_ZONE ){
          height = 20+15*(Sys.prjInfo.allZone.size()-1);  //zone 0 skipped
          title = "Shade By Zone";

          int sz=Sys.prjInfo.allZone.size();
          strLegend = new String[sz-1];
          colorLegend = new Color[sz-1];
          int cntr=0; //cntr = sz -1 eventually because zone=1 is skipped
          double dZone;
          NumberFormat nf = NumberFormat.getNumberInstance();
          nf.setGroupingUsed(false);
          for( int i=0;i<sz;i++){
              //tmp = ((Integer)Sys.prjInfo.zoneList.elementAt(i)).intValue();
              Zone z = (Zone) Sys.prjInfo.allZone.elementAt(i);
              dZone = z.getZoneNumber();
              if (dZone != 0.0) {
                  strLegend[cntr] = z.getZoneLabel();
                  if (strLegend[cntr] == null || strLegend[cntr].length() == 0)
                      strLegend[cntr] = "Zone " + nf.format(dZone);
                  colorLegend[cntr] = z.getZoneColor();
                  cntr++;
              }
          }
      }
      else if( Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_NOM_VOLTAGE ){
          // graphical output for Legend...set output format
          NumberFormat doubleFormat = NumberFormat.getNumberInstance();
          doubleFormat.setMaximumFractionDigits(2);
          doubleFormat.setMinimumFractionDigits(0);

          height = 20 + 15 * Sys.voltageLevels.size();
          title = "Nominal Voltage";

          strLegend = new String[Sys.voltageLevels.size()];
          colorLegend = new Color[Sys.voltageLevels.size()];
          for(int i=0 ; i<Sys.voltageLevels.size() ; i++) {
              Double d = (Double)Sys.voltageLevels.get(
                  Sys.voltageLevels.size()-1-i);
              strLegend[i]=doubleFormat.format(d.doubleValue())+" kV";
              colorLegend[i] = SysConst.shadingColors[
                  (i % (SysConst.shadingColors.length-1)) + 1];
          } // end of for
      }
      else{
          //draw nothing
          rectShadingBox.setBounds(x, y, 0, 0);
          return;
      };

      //draw white bakcground
      g.setColor(Color.white);
      //padding = 10 (Left, Right, Bottom, Top);
      g.fillRect(x,y, width + 2*padding, height+2*padding);

      g.setColor(Color.gray);
      g.drawRect( x+padding, y +padding, width, height);
      g.setColor(Color.white);
      g.fillRect( x+padding+15, y+padding-2, 120, 5);

      //draw title
      g.setColor(Color.black);
      g.drawString( title, x+padding+15,y+padding+5);

      //draw contents in shading legend
      for( int i=0;i<strLegend.length;i++){
          if (continuous && i < strLegend.length-1) {
              Color c1 = colorLegend[i];
              Color c2 = colorLegend[i+1];
              for( int j = 0; j < 15; j++){
                  double j2 = ((double)j) / 15;
                  double j1 = 1.0 - j2;
                  g.setColor(new Color((int)(c2.getRed()*j2  +c1.getRed()*j1),
                                       (int)(c2.getGreen()*j2+c1.getGreen()*j1),
                                       (int)(c2.getBlue()*j2 +c1.getBlue()*j1)));
                  g.fillRect(x+20, y+30+i*15+j, 40, 1);
              }
          } else {
              g.setColor( colorLegend[i]);
              g.fillRect( x+20, y+30+i*15, 40, 3);
          }
          g.setColor( Color.black);
          g.drawString(strLegend[i], x+70,y+35+i*15);
      }

      g.setColor( originalColor); //set color to original one
      g.setFont( originalFont );

      rectShadingBox.setBounds(x, y, width + 2*padding, height + 2*padding);
  }//end of paintShadingLegend();

  private void paintScale(Graphics g, Point leftTop){
      Color originalColor = g.getColor();
      Font  originalFont  = g.getFont();
      int x = leftTop.x;
      int y = leftTop.y;
      int width  = 150;
      int height = 30;
      int padding = 10;

      Font f = GuiConst.fontDefault;
      g.setFont(f);

      //draw white bakcground
      g.setColor(Color.white);
      g.fillRect( x, y, width+2*padding, height+2*padding);
      g.setColor(Color.gray);
      g.drawRect( x+padding, y+padding, width, height );
      g.setColor(Color.white);
      g.fillRect( x+padding+15, y+padding-2, 120, 5);

      //draw title
      g.setColor(Color.black);
      NumberFormat nf = NumberFormat.getNumberInstance();
      nf.setMaximumFractionDigits(4);
      nf.setGroupingUsed(false);
      String s = nf.format( Sys.prjInfo.lengthPerUnit
          * SysConst.SCALEBAR_LENGTH / Sys.prjInfo.dZoomFactor ) +" ";
      switch (Sys.prjInfo.unitOfLength ){
          case 0: s +="mi"; break;
          case 1: s +="km";   break;
          case 2: s +="kft";  break;
          case 3: s +="Ft";   break;
          default: s +="ERROR!";  break;
      }
      g.drawString( s, x+25,y+15);

      //draw contents in shading legend
      for(int i=0;i<4;i++){
          if( (i%2) == 0){
              g.setColor( Color.gray);
              g.fillRect( x+25+16*i, y+20, 16, 10);
          }
          g.setColor( Color.black);
          g.drawRect( x+25+16*i, y+20, 16, 10);
      }

      g.setColor( originalColor); //set color to original one
      g.setFont(originalFont);

        rectScaleBox.setBounds(x, y, width + 2*padding, height + 2*padding);
    }//end of paintScale();


//  private void paintGridLine(Graphics g){
//      Color originalColor = g.getColor();
//      g.setXORMode( new Color(230,230,235)); //Color.gray);
//
//      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect(); //drawing area
//      int left = newRect.x;
//      int top  = newRect.y;
//      int right= left + newRect.width;
//      int bottom=top  + newRect.height;
//      int a,b, x,y;
//      a = (int) Math.round( ((double)left)/Sys.prjInfo.dZoomFactor); //virtual resolution
//      x = (int) Math.round( a*Sys.prjInfo.dZoomFactor );        //back to physical resolution
//      while( x < right){
//          g.drawLine(x,top, x, bottom);
//          a=a + (int)Math.round(Sys.prjInfo.gridWidth);
//          x = (int) Math.round( a*Sys.prjInfo.dZoomFactor );
//      }
//
//      b = (int) Math.round( ((double)top)/Sys.prjInfo.dZoomFactor); //virtual resolution
//      y = (int) Math.round( b*Sys.prjInfo.dZoomFactor );        //back to physical resolution
//      while( y < bottom){
//          g.drawLine(left,y, right,y);
//          b =b+(int)Math.round(Sys.prjInfo.gridWidth);
//          y = (int) Math.round( b*Sys.prjInfo.dZoomFactor );
//      }
//
//      g.setPaintMode();
//      g.setColor(originalColor);
//  }

  private void paintGridLineInXorMode(Graphics g, int dx, int dy){
      Color originalColor = g.getColor();
      g.setXORMode( GuiConst.clrMyGray );

      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect(); //drawing area
      int left = newRect.x;
      int top  = newRect.y;
      int right= left + newRect.width;
      int bottom=top  + newRect.height;
      int a,b, x,y;
      a = (int) Math.round( ((double)left)/Sys.prjInfo.dZoomFactor); //virtual resolution
      x = (int) Math.round( a*Sys.prjInfo.dZoomFactor );        //back to physical resolution
      while( x < right){ //vertical lines
          g.drawLine( (x-dx),(top-dy), (x-dx), (bottom-dy));
          a=a + (int)Math.round(Sys.prjInfo.gridWidth);
          x = (int) Math.round( a*Sys.prjInfo.dZoomFactor );
      }

      b = (int) Math.round( ((double)top)/Sys.prjInfo.dZoomFactor); //virtual resolution
      y = (int) Math.round( b*Sys.prjInfo.dZoomFactor );        //back to physical resolution
      while( y < bottom){ //horizontal lines
          g.drawLine( (left-dx),(y-dy), (right-dx),(y-dy));
          b =b+(int)Math.round(Sys.prjInfo.gridWidth);
          y = (int) Math.round( b*Sys.prjInfo.dZoomFactor );
      }

      g.setPaintMode();
      g.setColor(originalColor);
  }

  private void paintText( Graphics g){
      AbsElem theElem;
      for(int i=0; i<Sys.screenElemList.size(); i++){
          theElem=(AbsElem) Sys.screenElemList.get(i);
          theElem.drawText(g);
      }
      for(int i=0; i<Sys.bgElemList.size(); i++){
          theElem=(AbsElem) Sys.bgElemList.get(i);
          theElem.drawText(g);
      }
  }//end of paintText()

  private void paintNodes( Graphics g){
      //always try to show nodes of BgLine & BgImage
      //BgLine shows nodes only if iActiveLayer == Bg_Layer
      //BgImage shows nodes only if it is selected
      AbsElem theElem;
      if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_IMAGE_LAYER){
          for(int i=0; i<Sys.imgList.size(); i++){
              theElem=(AbsElem) Sys.imgList.get(i);
              theElem.drawNodes(g);
          }
      }
      if( Sys.prjInfo.iActiveLayer == ProjectInfo.BG_LINE_LAYER){
          for(int i=0; i<Sys.bgElemList.size(); i++){
              theElem=(AbsElem) Sys.bgElemList.get(i);
              theElem.drawNodes(g);
          }
      }
      if( Sys.prjInfo.iActiveLayer > ProjectInfo.BG_LINE_LAYER){//Forground layers
          for(int i=0; i<Sys.screenElemList.size(); i++){
              theElem=(AbsElem) Sys.screenElemList.get(i);
              theElem.drawNodes(g);
          }
      }
  }//end of paintNodes()

  private void paintHighLighted( Graphics g) {
      if( Sys.prjInfo.iActiveLayer <= ProjectInfo.BG_LINE_LAYER ) { return; }

      AbsElem theElem;
      for(int i=0; i<Sys.screenElemList.size(); i++){
          theElem=(AbsElem) Sys.screenElemList.get(i);
          theElem.drawHighLights( g );
      }

      for( int i=0; i<Sys.highLightedElemList.size(); i++){
          theElem = (AbsElem) Sys.highLightedElemList.get(i);
          if( Sys.screenElemList.contains( theElem ) ){
              theElem.drawHighLights( g );
          }
      }
  }

  private void insertNodeForLineOrCable(int x, int y){
      AbsElem elem = Sys.findElem( x, y);
      if( elem==null || !(elem instanceof Line) ){
          JOptionPane.showMessageDialog(mainFrame,"For Lines or Cables only");
          return;
      }

      int x1,y1,x2,y2;
      double totalLen, firstLen, secondLen;
      Line oldLine = (Line) elem;
      LineGeom oldLineGeom = (LineGeom)oldLine.getElemGeomObj();
      totalLen=oldLine.getLength();

      //determine whether the length of one line will be 0
      int xStored,yStored;
      xStored = (int) Math.round(((float)x)/Sys.prjInfo.dZoomFactor); //as stored value
      yStored = (int) Math.round(((float)y)/Sys.prjInfo.dZoomFactor); //as stored value
      if( (xStored == oldLineGeom.x1Stored && yStored == oldLineGeom.y1Stored )||
          (xStored == oldLineGeom.x2Stored && yStored == oldLineGeom.y2Stored )){
          JOptionPane.showMessageDialog(mainFrame,"Can't insert node: too close to an end");
          return;
      }

      //create two new Line object
      Line newLine = (Line) oldLine.clone();
      LineGeom newLineGeom = (LineGeom)newLine.getElemGeomObj();
      Line newLine2 = (Line) oldLine.clone();
      LineGeom newLineGeom2 = (LineGeom)newLine2.getElemGeomObj();

      newLineGeom.setName(SysConst.ssTypeEngine[oldLine.getType()] + "_" +Sys.elemList.size());
      newLineGeom.setNode1FromScreen(oldLineGeom.x1,oldLineGeom.y1);
      newLineGeom.setNode2FromScreen(x,y);
      newLineGeom2.setName(SysConst.ssTypeEngine[oldLine.getType()] + "_" +Sys.elemList.size());
      newLineGeom2.setNode1FromScreen(x,y);  //!!!maybe need to set other data fields!!!
      newLineGeom2.setNode2FromScreen(oldLineGeom.x2,oldLineGeom.y2);

      //find the length of the two lines
      x1 = newLineGeom.x1;      y1 = newLineGeom.y1;
      x2 = newLineGeom.x2;      y2 = newLineGeom.y2;
      firstLen=Math.sqrt( (double)((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ) );
      x1 = newLineGeom2.x1;     y1 = newLineGeom2.y1;
      x2 = newLineGeom2.x2;     y2 = newLineGeom2.y2;
      secondLen=Math.sqrt( (double)((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ) );
      newLine.setData(AbsElem.DATA_LENGTH, totalLen * firstLen /(firstLen + secondLen));
      newLine2.setData(AbsElem.DATA_LENGTH, totalLen * secondLen /(firstLen + secondLen));

      Vector v = new Vector();
      v.add( oldLine );   //1st elem in v; DELETE
      v.add( newLine);    //2nd elem in v: ADD
      v.add( newLine2);   //3rd elem in v: ADD
      mainFrame.menuBar.pushIntoQueue( EditAction.INSERT_NODE, v);

      Sys.deleteElem( oldLine );
      Sys.addElem( newLine, true, true);
      Sys.addElem( newLine2, true, true);
      Sys.focusElem = newLine;

      mainFrame.mainPanel.needRedraw=true;
      mainFrame.mainPanel.repaint();
  }//end of insertNodeForLineOrCable()

  private Line canSmartAdd(AbsElem elem){
      AbsElemGeom geom = elem.getElemGeomObj();
      final double SMART_ADD_LENGTH_FACTOR = 1.5;
      if( !(geom instanceof TwoNodeElemGeom) ) { return null; }

      AbsElem e2= Sys.findElem(ptMsPressed.x,ptMsPressed.y);
      if( !(e2 instanceof Line) ){ return null;     }

      AbsElemGeom g2 = e2.getElemGeomObj();  //e2, g2 for Line
      if( g2.getVisualDist() <= SMART_ADD_LENGTH_FACTOR *geom.getVisualDist()){
        return null; //Line is too short
      }

      int avgX = (g2.x1Stored + g2.x2Stored )/2;
      if( g2.isStartPoint(geom.x1,geom.y1,SysConst.SNAP_SIZE)==true ||
          g2.isEndPoint(geom.x1,geom.y1,SysConst.SNAP_SIZE)==true){
          return null;
      }
      return (Line) e2;
  }

  private void doSmartAdd( AbsElem elem, Line line){
      AbsElemGeom geom = elem.getElemGeomObj();
      LineGeom g2 = (LineGeom) line.getElemGeomObj();
      double dist1 =(geom.x1Stored - g2.x1Stored)*(geom.x1Stored - g2.x1Stored);
      dist1 += (geom.y1Stored - g2.y1Stored)*(geom.y1Stored - g2.y1Stored);
      double dist2 =(geom.x1Stored - g2.x2Stored)*(geom.x1Stored - g2.x2Stored);
      dist2 += (geom.y1Stored - g2.y2Stored)*(geom.y1Stored - g2.y2Stored);

      if( dist1 < dist2 ){ //Math.abs(geom.x1Stored - g2.x1Stored) < Math.abs(geom.x1Stored - g2.x2Stored) ){
          geom.setNode1FromStored( g2.x1Stored, g2.y1Stored);
          //((TwoNodeElemGeom)geom).findNode2BasedonDir();//replaced with next 3 lines
          Point pt = findAdjustedPointForLineBreaking(
              g2.x1Stored, g2.y1Stored, g2.x2Stored, g2.y2Stored );
          ((TwoNodeElemGeom)geom).setNode2FromStored( pt);
          geom.calcFrame();
          g2.setNode1FromStored( geom.x2Stored, geom.y2Stored);
          g2.calcFrame();
      }else{//close to node2 of g2 (the line)
          geom.setNode1FromStored( g2.x2Stored, g2.y2Stored);
          //((TwoNodeElemGeom)geom).findNode2BasedonDir();
          Point pt = findAdjustedPointForLineBreaking(
              g2.x2Stored, g2.y2Stored, g2.x1Stored, g2.y1Stored );
          ((TwoNodeElemGeom)geom).setNode2FromStored( pt);
          geom.calcFrame();
          g2.setNode2FromStored( geom.x2Stored, geom.y2Stored);
          g2.calcFrame();
      }
      if( line.getAutoFlag(AbsElem.DATA_LENGTH) ){ line.setLengthToAutoValue(); }
  }//end of doSmartAdd


  /**
   * Applies to: a non-line, non-bus , non-group, and two-node elem
   * Also, this elem must be connected with a LONG line/cable at one node,
   * and connected with (>=1) elem at the other node.
   * LONG line/cable is defined as 4 time longer visually than this elem.
   */
  public AbsElem canSmartDelete(AbsElem elem){
      AbsElemGeom geom = elem.getElemGeomObj();
      if( Sys.prjInfo.iActiveLayer < 0 ) { return null; } //Bg layer
      if( !(geom instanceof TwoNodeElemGeom) || (geom instanceof GroupGeom)
          ||(geom instanceof BusGeom) || (geom instanceof LineGeom)) { return null; }
      int iMinConn = Math.min( geom.conn1.size(), geom.conn2.size() );
      int iMaxConn = Math.max( geom.conn1.size(), geom.conn2.size() );
      if( (iMinConn != 1) || (iMaxConn<1) ) { return null; }
      //to continue: min==1 && max>=1

      AbsElem elem2=null;
      AbsElemGeom geom2;
      if( geom.conn1.size() == 1 ) {
          elem2 = (AbsElem) geom.conn1.get(0);
          if( elem2 instanceof Line ) {
              geom = elem.getElemGeomObj();
              geom2 = elem2.getElemGeomObj();
              if( geom2.getVisualDist()>geom.getVisualDist()*4 ) {
                  return elem2;
              }
          }
      }
      if( geom.conn2.size() == 1 ) {
          elem2 = (AbsElem) geom.conn2.get(0);
          if( elem2 instanceof Line ) {
              geom = elem.getElemGeomObj();
              geom2 = elem2.getElemGeomObj();
              if( geom2.getVisualDist()>geom.getVisualDist()*4 ) {
                  return elem2;
              }
          }
      }
      return null;
  }

  public void doSmartDelete(AbsElem elem){
      AbsElemGeom geom, geom2;
      AbsElem elem2 = canSmartDelete( elem ); //the connected line
      if( elem2 == null ){
          Sys.deleteElem (elem);
      } else { //will do smart-delete
          geom = elem.getElemGeomObj();
          geom2 = elem2.getElemGeomObj();
          if( geom2.x1Stored==geom.x1Stored && geom2.y1Stored==geom.y1Stored ){
              geom2.setNode1FromStored( geom.x2Stored, geom.y2Stored);
          } else if( geom2.x1Stored==geom.x2Stored && geom2.y1Stored==geom.y2Stored ){
              geom2.setNode1FromStored( geom.x1Stored, geom.y1Stored);
          } else if( geom2.x2Stored==geom.x1Stored && geom2.y2Stored==geom.y1Stored ){
              geom2.setNode2FromStored( geom.x2Stored, geom.y2Stored);
          } else if( geom2.x2Stored==geom.x2Stored && geom2.y2Stored==geom.y2Stored ){
              geom2.setNode2FromStored( geom.x1Stored, geom.y1Stored);
          }
          if( elem2.getAutoFlag(AbsElem.DATA_LENGTH) ){
              ((Line)elem2).setLengthToAutoValue();
          }
          Sys.deleteElem(elem);
      }
  }//end of doSmartDelete();

  private Point findAdjustedPointForLineBreaking(int xStart, int yStart, int xEnd, int yEnd){
      double dist  = Math.sqrt( (double)((xEnd-xStart)*(xEnd-xStart) + (yEnd-yStart)*(yEnd-yStart) ) );
      double theta = Math.asin( ((double)(yEnd-yStart))/dist ); // range of asin: (-pi/2, pi/2)
      if( xEnd >=xStart && yEnd>=yStart )     { /* do nothing, b/c in (0-90) */   }
      else if( xEnd>=xStart && yEnd<yStart)   { /* do nothing, b/c in (0-90) */   }
      else if( xEnd<xStart && yEnd>=yStart )  { theta = Math.PI - theta;  }
      else /* (xEnd<xStart && yEnd<yStart)*/  { theta = -Math.PI - theta; }
      int a = (int) Math.round( xStart + 4 *Math.cos(theta)); //4 is stored_component_size
      int b = (int) Math.round( yStart + 4 *Math.sin(theta));
      return new Point(a,b);
  }
}//end of class DrawingPanel

//Trash Code
//  private void addElemToScreenImage( BgLine bgLine){
//      if( image == null) {
//          Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//          image= createImage( dim.width, dim.height );
//      }
//      Graphics g = image.getGraphics();
//      Rectangle newRect = mainFrame.scrollPane.getViewport().getViewRect();
//      bgLine.draw(g, newRect.x, newRect.y);
//      needRedraw=false; //redundent;
//  }

//  private Rectangle findClipRect(){
//      //compare 3 points: ptMsPressed, ptMsDraggedPrev, ptMsDragged
//      int  xClipLeftTop =  Math.min( ptMsDragged.x, ptMsPressed.x);
//      int  yClipLeftTop =  Math.min( ptMsDragged.y, ptMsPressed.y);
//
//      if( ptMsDraggedPrev.x !=0 && ptMsDraggedPrev.y !=0){
//           xClipLeftTop = Math.min( xClipLeftTop, ptMsDraggedPrev.x );
//           yClipLeftTop = Math.min( yClipLeftTop, ptMsDraggedPrev.y );
//      }// The first mouseDrag event will be skipped
//
//      int xClipBottomRight =Math.max( Math.max(ptMsDragged.x, ptMsPressed.x), ptMsDraggedPrev.x);
//      int yClipBottomRight =Math.max( Math.max(ptMsDragged.y, ptMsPressed.y), ptMsDraggedPrev.y);
//
//      Rectangle r =new Rectangle();
//      r.setLocation(xClipLeftTop -2 ,yClipLeftTop -2 );// "-2" & "+4": make the rect
//      r.setSize(  xClipBottomRight-xClipLeftTop+4,     // a bit larger than needed
//                  yClipBottomRight-yClipLeftTop+4);    // and make sure to cover everything
//      return r;
//  }